##  Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com 
##
##
##  This program is free software; you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation; either version 2 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program; if not, write to the Free Software
##  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import os
import sys
from types import *

#=============================================

def buidList(prefix,_list):
    l = ""
    for _l in _list:
        quote = ""
        if ' ' in _l:
            quote= '\"'
        l+=prefix+quote+_l.replace('\\','/')+quote+' '
    return l

#=============================================

def getObjFromCPP(cpp):
    return cpp.replace('.cpp','.o').replace('.cc','.o')

#=============================5================

def getObjsFromSource(src):
    line = ''
    for s in src:
        line+=getObjFromCPP(s)+' '
    return line

#=============================================

def buildProgramLine(compiler,objs,lib_dirs,flags,macros,libs,out,rc = ''):
    line = ''
    line+=compiler+' '
    line+=lib_dirs+' '
    line+='-o'+out+'.exe '
    line+=objs+' '
    line+=macros+' '
    line+=flags+' '
    line+=libs+' '
    line+=rc
    return line

#=============================================

def buildStaticLibLine(compiler,objs,out):
    sp = out.replace('\\','/').split('/')
    sp[-1] = 'lib'+sp[-1]
    out = ''
    for i in sp:
        out = os.path.join(out,i)
    line = ''
    line+=compiler+' -r '
    line+=out+'.a '
    line+=objs+' '
    return line

#=============================================

def getCPPCompileLine(compiler,flags,macro,cpp,inc):
    line = ''
    line+=compiler+' '
    line+=inc+' '
    line+=macro+' '
    if not '-c' in  flags:
        line+='-c '
    line+=flags+' '
    line+='-o'+getObjFromCPP(cpp)+' '
    line+=cpp
    return line

#=============================================

PROGRAM = 0
STATIC_LIB = 1

class Target:
    def __init__(self,trg):
        self._inc = []
        self._flags = ['O3','Wall','fmessage-length=0']
        self._macros = []
        self._lib_dirs = []
        self._cpp = []
        self._libs = []
        self._out = 'hello_world'
        self._bt = PROGRAM
        self._trg = trg

        self.__resource = None
    

    def setMetaData(self,
                    file_version = '1,0,0,0',
                    comp_name = 'OpenSource',
                    copy_right = 'OpenSource',
                    discript = '',
                    icon = None
                    ):
        self.__resource = {}
        self.__resource['file_version']=str(file_version)
        self.__resource['comp_name']=str(comp_name)
        self.__resource['copy_right']=str(copy_right)
        self.__resource['discrip']=str(discript)
        self.__resource['icon']=icon

    def setOutput(self,out):
        self._out = out

    def setTargetType(self,t):
        global PROGRAM,STATIC_LIB
        if not t in [PROGRAM,STATIC_LIB]:
            raise Exception("Invalid Value !!! Must be make.PROGRAM or make.STATIC_LIB")
        self._bt = t
        
    def add(self,INC = [],
            FLAGS = [],
            MACRO = [],
            LIB_DIR = [],
            LIBS = [],
            CPP = []):
        self.__append_to_list(self._inc,INC)
        self.__append_to_list(self._flags,FLAGS)
        self.__append_to_list(self._macros,MACRO)
        self.__append_to_list(self._lib_dirs,LIB_DIR)
        self.__append_to_list(self._libs,LIBS)
        self.__append_to_list(self._cpp,CPP)

    def __append_to_list(self,l,files):
        if type(files) is ListType:
            for f in files:
               if not f in l:
                   l.append(f)
        elif type(files) is StringType:
            if not files in l:
                l.append(files)
        else:
            raise Exception ("files must be String or a list of Strings!!!!" )
        
    def getSettings(self):
        return {'INC' : self._inc,
            'FLAGS' : self._flags,
            'MACRO' : self._macros,
            'LIB_DIR' : self._lib_dirs,
            'LIBS' : self._libs,
            'CPP' : self._cpp,
            'OUT':self._out,
            'BT':self._bt,
            'TRGT':self._trg,
            'RC' : self.__resource}


#=============================================
class Make:
    def __init__(self,compiler='g++',ar_compiler='ar'):
        self._ar_compiler = ar_compiler
        self._compiler = compiler
        self.targets = {}
        self.lines = ''

    def clear(self):
        self.targets = {}
        self.lines = ''

    def addTarget(self,target):
        s = target.getSettings()
        self.targets[s['TRGT']] = []
        for cpp in s['CPP']:
             line = getCPPCompileLine(self._compiler,
                            buidList('-',s['FLAGS']),
                            buidList('-D',s['MACRO']),
                            cpp,
                            buidList('-I',s['INC']))
             self.targets[s['TRGT']].append(line)

        if s['BT'] == PROGRAM:
            rc = ''
            if not s['RC'] is None:
                self.__createResourceFile(s['RC'],s['OUT'])
                line = "windres %s -O coff -o %s"%(s['OUT']+'.rc',s['OUT']+'.res')
                self.targets[s['TRGT']].append(line)
                rc = s['OUT']+'.res'
                 
            line = buildProgramLine(self._compiler,
                            getObjsFromSource(s['CPP']),
                            buidList('-L',s['LIB_DIR']),
                            buidList('-',s['FLAGS']),
                            buidList('-D',s['MACRO']),
                            buidList('-l',s['LIBS']),
                            s['OUT'],
                            rc)
            self.targets[s['TRGT']].append(line)
        else:
            line = buildStaticLibLine(self._ar_compiler,
                            getObjsFromSource(s['CPP']),
                            s['OUT'])
            self.targets[s['TRGT']].append(line)
    
    def getLines(self):
        for t in self.targets.keys():
            self.lines+=t+' : \n'
            for l in self.targets[t]:
                self.lines+='\t'+l+'\n'
        return self.lines

    def __createResourceFile(self,rc,out):
        template = """
%(icon)s

1 VERSIONINFO
FILEVERSION     %(file_version)s
PRODUCTVERSION  %(file_version)s
BEGIN
  BLOCK "StringFileInfo"
  BEGIN
    BLOCK "080904E4"
    BEGIN
      VALUE "CompanyName", "%(comp_name)s"
      VALUE "FileDescription", "%(discrip)s"
      VALUE "FileVersion", "%(file_version)s"
      VALUE "InternalName", ""
      VALUE "LegalCopyright", "%(copy_right)s"
      VALUE "OriginalFilename", ""
      VALUE "ProductName", ""
      VALUE "ProductVersion", ""
    END
  END

  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x809, 1252
  END
END
"""
        icon = rc['icon']
        if rc['icon'] is None:
            rc['icon'] = ''
        else:
            rc['icon'] = "id ICON \""+rc['icon']+"\""

        data = template % rc

        rc['icon'] = icon

        fh=open(out+'.rc','w')
        fh.write(data)
        fh.close()
        return out+'.rc'

#=============================================

class Project:
    def __init__(self,n=""):
        self.name = n
        self.trgs = {}
        self.__make = Make()
        self.__gt = []
        self.__env = 'mingw'

    def setEnviroment(self,e):
        self.__env = e

    def getEnviroment(self):
        return self.__env
        
    def genMakeFile(self):
        self.__make.clear()
        for t in self.trgs.keys():
                self.__make.addTarget(self.trgs[t])
        lines = self.__make.getLines()
        for l in self.__gt:
            lines+='\n'+l
        fh=open('make'+sys.platform,'w')
        fh.write(lines)
        fh.close()

    def addTarget(self,t,OUT = None,TYPE=PROGRAM):
        if OUT is None:
            OUT = t
        self.trgs[t] = Target(t)
        self.trgs[t].setTargetType(TYPE)
        self.trgs[t].setOutput(OUT)

    def createGroupTarget(self,trg,group):
        line = trg +' : '
        for g in group:
            line += g+' '
        self.__gt.append(line)

    def setMetaData(self,trg,
                    file_version = '1,0,0,0',
                    comp_name = 'OpenSource',
                    copy_right = 'OpenSource',
                    discript = '',
                    icon = None
                    ):
        self.trgs[trg].setMetaData(
                    file_version ,
                    comp_name ,
                    copy_right ,
                    discript,
                    icon
                    )

    def add(self,target = None,INC = [],
            FLAGS = [],
            MACRO = [],
            LIB_DIR = [],
            LIBS = [],
            CPP = []):
        if target is None:
            for t in self.trgs.keys():
                self.trgs[t].add(INC,
                                FLAGS,
                                MACRO,
                                LIB_DIR,
                                LIBS,
                                CPP)
        else:
            self.trgs[target].add(INC,
                            FLAGS,
                            MACRO,
                            LIB_DIR,
                            LIBS,
                            CPP)

    def build(self,trg=""):
        self.genMakeFile()
        make = 'mingw32-make'
        os.system(make+' -f '+'make'+sys.platform+' '+trg)
        
        

#=============================================

def main():
    inc = ['C:/','c:/program files']
    flags = ['O3','Wall','fmessage-length=0']
    macros = []
    lib_dirs = ['C:/','c:/program files']
    cpp = ['test/main.cpp','test/func.cpp']
    compiler = 'g++'
    rcompiler = 'ar'
    libs = []
    out = 'hello_world'
    make = Make()
   
    env = Project()
    env.addTarget('all'
##                  ,TYPE = STATIC_LIB
                  )
    env.setMetaData('all')
    env.add(INC=inc,
            FLAGS=flags,
            MACRO=macros,
            LIB_DIR=lib_dirs,
            LIBS=libs,
            CPP=cpp)

    env.build('all')

    
    
#=============================================

if __name__ == '__main__':
    main()
